package DigitalNetwork;

import java.io.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.SwingWorker;

/**
 * A digitalis halozatot megvalosito osztaly.
 * <p>
 * A szimulalni kivant halozatot itt toltjuk be.
 * A halozat megfelelo huzalozasat is ebben az osztalyban ellenorizzuk le.
 * A simulatort itt inditjuk el.
 * </p>
 *
 * @author Ableda Peter
 * @author Andrasi Istvan
 * @author Kamaras Karoly
 * @author Pora Szilard
 *
 * @version 1.0 Build 2011.04.17.
 */
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public class Network {

	/** A halozat szimulacijat vezerli.*/
	public Simulator simulator;
	/** Ezen az interfeszen kommunikál a controllerrel.*/
	private INetworkListener listener = null;

	/** [Konstruktor]
	 * Letrehozz egy szimulatort.
	 */
	//==========================================================================
	public Network() {
		try {
			// Letrehozunk egy szimulatort.
			simulator = new Simulator();
			// Ertesitunk.
			Main.printMessage("#Network letrehozva");
		} catch (Exception e) {
		}
	}
	//==========================================================================

	/**
	 * 
	 */
	public class SimulatingTask extends SwingWorker<String, Object> {

		/**
		 * 
		 */
		@Override
		public String doInBackground() {
			while (!isCancelled()) { // Leptetjuk a szimulaciot.

				simulator.nextIteration();
				// Frissitjuk a kijelzoket.
				simulator.collectValues();
				if (listener != null) {
					listener.onIteration();
				}

				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
				}

			}
			return "Done.";
		}
	}

	/**
	 * Ellenorzi, hogy a halozat megfeleloen van-e behuzalozva
	 * @return
	 */
	//==========================================================================
	public boolean validate() {
		return this.simulator.check();
	}
	//==========================================================================

	/**
	 * Leszimulal iterations szamu lepest.
	 * <p>
	 * Addig leptetjuk az iteration-t amig 0 nem lesz.
	 * </p>
	 *
	 * @param iterations Lepesek szama.
	 */
	//==========================================================================
	public void startSimulation(int iterations) {
		while (iterations > 0) {
			// Ertesitunk.
			System.out.println("[Iteracio]");
			System.out.println("Bemenet:");

			// Kiirjuk a jelforrasok allapotat.
			simulator.list("Source");
			// Leptetjuk a szimulaciot.
			simulator.nextIteration();
			// Frissitjuk a kijelzoket.
			simulator.collectValues();

			// Ertesitunk.
			System.out.println("Kimenet:");

			// Kiirjuk a kijelzok allapotat.
			simulator.list("Display");

			--iterations;

			if (this.listener != null) {
				this.listener.onIteration();
			}
		}
	}
	//==========================================================================

	/**
	 * Beolvas egy file-t majd az osszes soran vegeglepked a file vegeig,
	 * es vegrehajtja a kiolvasott parancsot.
	 * @param fname A file neve amibol a parancsokat olvassuk
	 * @param isComposite Ha igaz, akkor a Composite elemnek megfelelo, belso
	 * compositeInput es compositeOutput be/kimenetek jonnek letre
	 * Az isComposite parametert tovabbadjuk a parancsvegrehajto metodusnak.
	 */
	//==========================================================================
	public boolean loadNetwork(String fname, boolean isComposite) {
		// Inicializaljuk a bemenetet
		BufferedReader input = null;
		FileReader fr = null;
		try {
			// Letrehozzuk a filereadert a megadott file-nev alapjan
			fr = new FileReader("networks/" + fname);
			// Megnyitjuk a bemeneti file-t
			input = new BufferedReader(fr);
			// Elore olvasunk a file-bol
			String line = Main.readNextLine(input);

			// Amig nem erunk ures sorhoz, vegrehajtjuk a kiolvasott parancsot
			while (line != null) {
				// Tovabbadjuk, hogy composite elemben vagyunk-e
				fileinteract(line, isComposite);
				line = Main.readNextLine(input);
			}

			// Bezarjuk az input file-t
			input.close();

			// Igazzal terunk vissza, ha ervenyes a halozat, hamissal kulonben
			boolean success = this.validate();
			if (this.listener != null) {
				this.listener.onLoad(success);
			}
			return success;
		} catch (FileNotFoundException fne) {
			Main.printMessage("#[HIBA] Nem talalhato file: "
				+ fne.getMessage());
			return false;
		} catch (IOException ioe) {
			Main.printMessage("#[HIBA] I/O hiba: " + ioe.getMessage());
			return false;
		} catch (Exception e) {
			Main.printMessage("#[HIBA] " + e.getMessage());
			return false;
		}
	}
	//==========================================================================

	/**
	 * A file-bol beolvasott sort szettordeli egy string tombre.
	 * megjegyzes: a tordelest szokozok jelzik, igy allapithato meg,
	 * hogy egy parancsnak hany parametere van.
	 * Az isComposite parametert tovabbadjuk a parancsvegrehajto metodusnak.
	 */
	//==========================================================================
	private void fileinteract(String in, boolean isComposite)
		throws java.io.IOException {
		String tmp = in.trim();
		String[] input = tmp.split("\\s+");

		// A file-bol kiolvasott parancsok vegrehajtasa, tovabbadjuk, hogy
		//composite elemben vagyunk-e
		filecommandstatemachine(input, isComposite);
	}
	//==========================================================================

	/**
	 * A beolvasott sorbol keszult szettordelt string ertelmezeset megvalosito
	 * fuggveny.
	 *
	 * @param input A beolvasott sor.
	 * @param isComposite Ha igaz, akkor ennek megfelelo composite be/kimenetek
	 * jonnek letre
	 */
	//==========================================================================
	public void filecommandstatemachine(String[] input, boolean isComposite) {
		// ha nem letezik az input -> nincs parancs
		if (input == null) {
			Main.printMessage("#[HIBA] Nincs megadott parancs.");
			return;
			//======================================================================
			// ha ures az input -> nincs parancs
		} else if (input[0].equals("")) {
			Main.printMessage("#[HIBA] Nincs megadott parancs.");
			return;
			//======================================================================
			//ES kaput hozzaado parancs
		} else if (input[0].equals("addAndGate")) {
			// parameterek szamanak az ellenorzese
			if (input.length > 5) {
				Main.printMessage("#[HIBA] Tul sok parameter.");
				return;
			} else if (input.length < 5) {
				Main.printMessage("#[HIBA] Tul keves parameter.");
				return;
			} else if (!input[3].matches("\\d+") || !input[4].matches("\\d+")) {
				Main.printMessage("#[HIBA] A koordinatak ertekeinek szamot "
					+ "kell megadni.");
				return;
			} else {
				// az utolso parameter vizsgalata, hogy szam-e
				if (input[2].matches("\\d+")) {
					// ANDGate hozzadasa a halozathoz
					this.simulator.addUnit(new ANDGate(input[1],
						Integer.parseInt(input[2]),
						Integer.parseInt(input[3]),
						Integer.parseInt(input[4])));
				} else {
					Main.printMessage("#[HIBA] A bemenetek szamahoz szamot "
						+ "kell megadni!");
					return;
				}
			}
			//======================================================================
			//VAGY kaput hozzaado parancs
		} else if (input[0].equals("addOrGate")) {
			// parameterek szamanak az ellenorzese
			if (input.length > 5) {
				Main.printMessage("#[HIBA] Tul sok parameter.");
				return;
			} else if (input.length < 5) {
				Main.printMessage("#[HIBA] Tul keves parameter.");
				return;
			} else if (!input[3].matches("\\d+") || !input[4].matches("\\d+")) {
				Main.printMessage("#[HIBA] A koordinatak ertekeinek szamot "
					+ "kell megadni.");
				return;
			} else {
				// az utolso parameter vizsgalata, hogy szam-e
				if (input[2].matches("\\d+")) {
					// ORGate hozzaadasa a halozathoz
					this.simulator.addUnit(new ORGate(input[1],
						Integer.parseInt(input[2]),
						Integer.parseInt(input[3]),
						Integer.parseInt(input[4])));
				} else {
					Main.printMessage("#[HIBA] A bemenetek szamahoz szamot "
						+ "kell megadni!");
					return;
				}
			}
			//======================================================================
			//NOT kaput hozzaado parancs
		} else if (input[0].equals("addNotGate")) {
			// parameterek szamanak az ellenorzese
			if (input.length > 4) {
				Main.printMessage("#[HIBA] Tul sok parameter.");
				return;
			} else if (input.length < 4) {
				Main.printMessage("#[HIBA] Tul keves parameter.");
				return;
			} else if (!input[2].matches("\\d+") || !input[3].matches("\\d+")) {
				Main.printMessage("#[HIBA] A koordinatak ertekeinek szamot "
					+ "kell megadni.");
				return;
			} else {
				// NOTGate hozzaadasa a halozathoz
				this.simulator.addUnit(new NOTGate(input[1],
					Integer.parseInt(input[2]),
					Integer.parseInt(input[3])));
			}
			//======================================================================
			//Gombot hozzaado parancs
		} else if (input[0].equals("addButton")) {
			// parameterek szamanak az ellenorzese
			if (input.length > 4) {
				Main.printMessage("#[HIBA] Tul sok parameter.");
				return;
			} else if (input.length < 4) {
				Main.printMessage("#[HIBA] Tul keves parameter.");
				return;
			} else if (!input[2].matches("\\d+") || !input[3].matches("\\d+")) {
				Main.printMessage("#[HIBA] A koordinatak ertekeinek szamot "
					+ "kell megadni.");
				return;
			} else {
				Source s;
				// Ha composite elem network-jeben vagyunk, akkor nem sima
				// buttont hozunk letre, hanem CompositeInputot
				if (isComposite) {
					s = new CompositeInput(input[1],
						Integer.parseInt(input[2]),
						Integer.parseInt(input[3]));
				} else {
					s = new Button(input[1],
						Integer.parseInt(input[2]),
						Integer.parseInt(input[3]));
				}

				// Button/CompositeInput hozzaadasa a halozathoz
				this.simulator.addUnit(s);

				// Button/CompositeInput hozzaadasa a simulator
				// bemeneti listajahoz
				this.simulator.addSource(s);
			}
			//======================================================================
			//Generatort hozzaado parancs
		} else if (input[0].equals("addGenerator")) {
			// parameterek szamanak az ellenorzese
			if (input.length > 4) {
				Main.printMessage("#[HIBA] Tul sok parameter.");
				return;
			} else if (input.length < 4) {
				Main.printMessage("#[HIBA] Tul keves parameter.");
				return;
			} else if (!input[2].matches("\\d+") || !input[3].matches("\\d+")) {
				Main.printMessage("#[HIBA] A koordinatak ertekeinek szamot "
					+ "kell megadni.");
				return;
			} else {
				Source s;
				// Ha composite elem network-jeben vagyunk, akkor nem sima
				// buttont hozunk letre, hanem CompositeInputot
				if (isComposite) {
					s = new CompositeInput(input[1],
						Integer.parseInt(input[2]),
						Integer.parseInt(input[3]));
				} else {
					s = new Generator(input[1],
						Integer.parseInt(input[2]),
						Integer.parseInt(input[3]));
				}

				// Generator/CompositeInput hozzaadasa a halozathoz
				this.simulator.addUnit(s);

				// Generator/CompositeInput hozzaadasa a simulator
				// bemeneti listajahoz
				this.simulator.addSource(s);
			}
			//======================================================================
			//LED-t hozzaado parancs
		} else if (input[0].equals("addLed")) {
			// parameterek szamanak az ellenorzese
			if (input.length > 4) {
				Main.printMessage("#[HIBA] Tul sok parameter.");
				return;
			} else if (input.length < 4) {
				Main.printMessage("#[HIBA] Tul keves parameter.");
				return;
			} else if (!input[2].matches("\\d+") || !input[3].matches("\\d+")) {
				Main.printMessage("#[HIBA] A koordinatak ertekeinek szamot "
					+ "kell megadni.");
				return;
			} else {
				Display d;
				// Ha composite elem network-jeben vagyunk, akkor nem sima
				// buttont hozunk letre, hanem CompositeInputot
				if (isComposite) {
					d = new CompositeOutput(input[1],
						Integer.parseInt(input[2]),
						Integer.parseInt(input[3]));
				} else {
					d = new LED(input[1],
						Integer.parseInt(input[2]),
						Integer.parseInt(input[3]));
				}

				// LED/CompositeOutput hozzaadasa a halozathoz
				this.simulator.addUnit(d);

				// LED/CompositeOutput hozzaadasa a simulator
				// bemeneti listajahoz
				this.simulator.addDisplay(d);
			}
			//======================================================================
			//Oszcilloszkopot hozzaado parancs
		} else if (input[0].equals("addOszcilloszkop")) {
			// parameterek szamanak az ellenorzese
			if (input.length > 4) {
				Main.printMessage("#[HIBA] Tul sok parameter.");
				return;
			} else if (input.length < 4) {
				Main.printMessage("#[HIBA] Tul keves parameter.");
				return;
			} else if (!input[2].matches("\\d+") || !input[3].matches("\\d+")) {
				Main.printMessage("#[HIBA] A koordinatak ertekeinek szamot "
					+ "kell megadni.");
				return;
			} else {
				Display d;
				// Ha composite elem network-jeben vagyunk, akkor nem sima
				// buttont hozunk letre, hanem CompositeInputot
				if (isComposite) {
					d = new CompositeOutput(input[1],
						Integer.parseInt(input[2]),
						Integer.parseInt(input[3]));
				} else {
					d = new Oscilloscope(input[1],
						Integer.parseInt(input[2]),
						Integer.parseInt(input[3]));
				}

				// Oscilloscope/CompositeOutput hozzaadasa a halozathoz
				this.simulator.addUnit(d);

				// Oscilloscope/CompositeOutput hozzaadasa a simulator
				// bemeneti listajahoz
				this.simulator.addDisplay(d);
			}
			//======================================================================
			//Composite hozzaado parancs
		} else if (input[0].equals("addComposite")) {
			// parameterek szamanak az ellenorzese
			if (input.length > 5) {
				Main.printMessage("#[HIBA] Tul sok parameter.");
				return;
			} else if (input.length < 5) {
				Main.printMessage("#[HIBA] Tul keves parameter.");
				return;
			} else if (!input[3].matches("\\d+") || !input[4].matches("\\d+")) {
				Main.printMessage("#[HIBA] A koordinatak ertekeinek szamot "
					+ "kell megadni.");
				return;
			} else {
				// compostite elem betoltese file-bol
				// ha a composite halozatat tartalmazo file nem letezik,
				// sikertelen a parancsvegrehajtas
				try {
					FileReader f = new FileReader("networks/" + input[2]);
					if (f != null) {
						this.simulator.addUnit(new Composite(input[1], input[2],
							Integer.parseInt(input[3]),
							Integer.parseInt(input[4])));
					}
				} catch (IOException e) {
				}
			}
			//======================================================================
			// 2 elem osszakpcsolasara szolgalo parancs
		} else if (input[0].equals("connect")) {
			// parameterek szamanak az ellenorzese
			if (input.length > 5) {
				Main.printMessage("#[HIBA] Tul sok parameter.");
				return;
			} else if (input.length < 5) {
				Main.printMessage("#[HIBA] Tul keves parameter.");
				return;
			} else {
				// az portok vizsgalata, hogy szamok-e
				if (input[2].matches("\\d+") && input[4].matches("\\d+")) {
					//ha a ket megadott unit lezetik, akkor osszekotjuk oket
					Unit u1 = this.simulator.getUnit(input[1]);
					Unit u2 = this.simulator.getUnit(input[3]);
					if ((u1 != null) && (u2 != null)) {
						this.simulator.connectUnits(
							u1, Integer.parseInt(input[2]),
							u2, Integer.parseInt(input[4]));
					} else {
						Main.printMessage("#[HIBA] #" + input[1] + " vagy #" + input[3] + " unit nem letezik.");
						return;
					}
				} else {
					Main.printMessage("#[HIBA] A bemenetekhez szamot "
						+ "kell megadni!");
					return;
				}
			}
			//======================================================================
			// Invalid command
		} else {
			Main.printMessage("#[HIBA] Ismeretlen parancs.");


			return;


		}

	}
	//==========================================================================

	/**
	 * Listener hozzaadasa a halozathoz.
	 * Ezen keresztul kommunikal a controllerrel.
	 *
	 * @param l Halozathoz hozaadando listener.
	 */
	//==========================================================================
	public void addListener(INetworkListener l) {
		this.listener = l;

	}
	//==========================================================================
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

